/***************************************************************************
 *   This file is part of Colorbar.                                        *
 *   Copyright (C) 2009 Pim Schellart <P.Schellart@science.ru.nl>          *
 *   Copyright (C) 2009 Wilbert van Ham <W.vanHam@astro.ru.nl>             *
 *                                                                         *
 *   Colorbar is free software: you can redistribute it and/or modify      *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation, either version 3 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   Colorbar is distributed in the hope that it will be useful,           *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with Colorbar.  If not, see <http://www.gnu.org/licenses/>.     *
 ***************************************************************************/

#ifndef COLORBAR_H
#define COLORBAR_H

#include <QFile>

#include <QMouseEvent>
#include <QVector>
#include <QWidget>

#include <math.h>

#include "qfitsplugin.h"
#include "pluginmanager.h"

/**
 * Implements a colorbar with numerical scale.
 */
class Colorbar : public QWidget, public QFitsPlugin
{
    Q_OBJECT
	Q_INTERFACES(QFitsPlugin)
public:
    /**
     * Colorbar parameters.
     */
    enum baseSize
    {
        WINDOW_START_X=0,   /**< logical coordinates x start,*/
        WINDOW_START_Y=0,   /**< logical coordinates y start,*/
        WINDOW_WIDTH=1000,  /**< width of logical coordinates window,*/
        WINDOW_HEIGHT=100,  /**< height of logical coordinates window,*/
        GRADIENT_START_Y=40,/**< y direction start position of gradient relative to top of window,*/
        GRADIENT_END_Y=40,  /**< y direction end position of gradient relative to top of window,*/
        COLORBAR_HEIGHT=80, /**< height of colorbar relative to top of window,*/
        MAJOR_TICK_START=80,/**< y direction start of major ticks,*/
        MAJOR_TICK_END=85,  /**< y direction end of major ticks,*/
        MINOR_TICK_START=80,/**< y direction start of minor ticks (not implemented),*/
        MINOR_TICK_END=82,  /**< y direction end of minor ticks (not implemented),*/
        TEXT_START_X=-20,   /**< start of tick text bounding rectangle,*/
        TEXT_START_Y=80,    /**< start of tick text bounding rectangle,*/
        TEXT_WIDTH=40,      /**< width of tick text bounding rectangle,*/
        TEXT_HEIGHT=30      /**< height of tick text bounding rectangle.*/
    };

    /**
     * Constructor.
     *
     * Generates new Colorbar with outer range from 0 to 65536, inner center 0.0 and inner with 1.0.
     * Colorbar is monochrome with white left and black right.
     */
    Colorbar();

    /**
     * Constructor.
     *
     * Generates new Colorbar.
     *
     * @param outerL left value (any iteger)
     * @param outerR right value (any integer)
     * @param innerCenter center of inner range (-0.5 to 0.5 is visible)
     * @param innerWidth width of inner range (1.0 is fully visible, negative values mirror colors)
     *
     * Colorbar is monochrome with white left and black right.
     */
    Colorbar(int outerL, int outerR, double innerCenter, double innerWidth);

	/**
	 * Implementation of QFitsPlugin
	 */
	void onInitialize( QObject *manager );

public slots:
    /**
     * Sets left value of outer range and updates colorbar.
     *
     * @param outerL outer left value
     */
    inline void setOuterL(int outerL) { _outerL=outerL; emit(outerLChanged(outerL)); update(); }

    /**
     * Sets right value of outer range and updates colorbar.
     *
     * @param outerR outer right value
     */
    inline void setOuterR(int outerR) { _outerR=outerR; emit(outerRChanged(outerR)); update(); }

    /**
     * Sets center of inner range.
     *
     * @param center (between -0.5 and 0.5 is visible)
     */
    inline void setInnerCenter(double center) { _innerCenter=center; emit(innerCenterChanged(center)); update(); }

    /**
     * Sets width of inner range.
     *
	 * @param width (between -1.0 and 1.0 is fully visible, negative value mirrors colors)
     */
	inline void setInnerWidth(double width) { _innerWidth=width; emit(innerWidthChanged(width)); update(); }

	/**
	 * Sets center and width of inner range. Does not emit any signals to prevent infinite loops.
	 *
	 * @param midValue Relative value of 50% gray (between 0 and 1.0)
	 * @param scale (d gray /d value) Between -1.0 and 1.0 is fully visible if cemter=0.5.
	 *        Negative value mirrors colors. Legal range -infinity -- infinity.
	 */
	inline void setInnerGray(double midValue, double grayScale)
	{
		if(((PluginManager *)sender())->getSender() == this)  // shot at self
			return;
		_innerCenter = midValue-.5;
		if(fabs(grayScale)>1E-6)
			_innerWidth=-1./grayScale; // note the - sign, maximum gray is white, minimal gray is black
		else
			_innerWidth = grayScale<0?1E6:-1E6; // prevent infinity if scale=0
		update();
	}

	/**
     * Sets color map for colorbar.
     *
     * @param ColorMap (QVector with QColor entries, colorbar gradient interpolates between colors)
     */
	inline void setColorMap(QVector<QColor> ColorMap) {
		QFile file;
		printf("setColorMap\n");
		int i=0;
		foreach(QColor color, ColorMap){
			printf("  color %d, %6X\n", i++, color.rgb());
		}
		_colorMap=ColorMap; update(); }

signals:
    /**
     * Emitted when outer range left value has changed.
     *
     * @param outerL outer range left value
     */
    void outerLChanged(int outerL);

    /**
     * Emitted when outer range right value has changed.
     *
     * @param outerR outer range right value
     */
    void outerRChanged(int outerR);

    /**
     * Emitted when inner range center position has changed.
     *
     * @param center inner range center position
     */
    void innerCenterChanged(double center);

	/**
	 * Emitted when inner range width has changed.
	 *
	 * @param width inner range width
	 */
	void innerWidthChanged(double width);

	/**
	 * Emitted when inner range width or position has changed.
	 *
	 * @param midValue Relative value of 50% gray (between 0 and 1.0)
	 * @param scale (d whiteness /d value) Between -1.0 and 1.0 is fully visible if midValue=0.5.
	 *        Negative value mirrors colors. Legal range -infinity -- infinity.
	 */
	void innerGrayChanged(double midValue, double grayScale);

protected:
    /**
     * Paints colorbar on widget.
     */
    void paintEvent(QPaintEvent *event);

    /**
     * Sets inner range center positon (left mouse button),
     * or inner range width (left mouse button + ctrl/cmd) responing to mouse click.
     * Center position is set in range -0.5 to 0.5 when clicked in colorbar window.
     * Width is set in range 1.0 to -1.0 when clicked in colorbar window.
     */
    void mousePressEvent(QMouseEvent *event);

    /**
     * Sets inner range center positon (left mouse button),
     * or inner range width (left mouse button + ctrl/cmd) responing to mouse move.
     * Center position is set in range -0.5 to 0.5 when clicked and moved in colorbar window,
     * but also allows center to be dragged out of window (up to 0.5+width so entire colorbar can become single color).
     * Width is set in range 1.0 to -1.0 when clicked and moved in colorbar window.
     */
    void mouseMoveEvent(QMouseEvent *event);

    /**
     * Default size hint for widget = 1000x100 corresponding to logical coordinates.
     */
    QSize sizeHint() const;

private:
    /**
     * Gives logical coordinate for specified outer range value.
     *
     * @param value outer range value
     * @return logical coordinate
     */
    inline int coordinateFromValue(int value) { return (value*Colorbar::WINDOW_WIDTH)/(_outerR-_outerL); }

    /**
     * Gives outer range value from logical window coordinate.
     *
     * @param coord logical window coordinate
     * @return outer range value
     */
    inline int valueFromCoordinate(int coord) { return (coord*(_outerR-_outerL))/Colorbar::WINDOW_WIDTH; }

    /**
     * Gives gradient coordinate in range (0,1) corresponding to coordinate in range (-(width+0.5),(width+0.5)).
     *
     * @param point coordinate in range (-(width+0.5),(width+0.5)
     * @return gradient position in range (0,1)
     */
    double normalize(double point);

    /**
     * Gives logical coordinate for clicked position in viewport.
     *
     * @param point coordinate in viewport coordinate system
     * @return logical coordinate in window coordinate system
     */
    double normFromAbsoluteCoordinates(QPoint point);

    int _outerL;
    int _outerR;

    double _innerCenter;
    double _innerWidth;

    QRect _viewport;
    QRect _window;

    QVector<QColor> _colorMap;
};

#endif // COLORBAR_H
